package com.ourteam.scm.utils;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.filefilter.TrueFileFilter;
import org.apache.commons.lang.SystemUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.tigris.subversion.javahl.StatusKind;
import org.tigris.subversion.svnclientadapter.ISVNClientAdapter;
import org.tigris.subversion.svnclientadapter.ISVNDirEntry;
import org.tigris.subversion.svnclientadapter.ISVNInfo;
import org.tigris.subversion.svnclientadapter.ISVNLogMessage;
import org.tigris.subversion.svnclientadapter.ISVNLogMessageChangePath;
import org.tigris.subversion.svnclientadapter.ISVNStatus;
import org.tigris.subversion.svnclientadapter.SVNNodeKind;
import org.tigris.subversion.svnclientadapter.SVNProgressEvent;
import org.tigris.subversion.svnclientadapter.SVNRevision;
import org.tigris.subversion.svnclientadapter.SVNUrl;
import org.tmatesoft.svn.core.wc.SVNWCUtil;

import com.ourteam.frame.util.WorkThread;
import com.ourteam.scm.utils.ResourceDiffentInfomation.DiffDetail;
import com.ourteam.sourcecode.utils.SourceLineBean;
import com.ourteam.sourcecode.utils.SourceLineCounter;

public class SVNServiceImpl extends AbstractSCMService {

	/**
	 * Logger for this class
	 */
	private static final Log logger = LogFactory.getLog(SVNServiceImpl.class);

	public SVNServiceImpl(SCMServerBean scmServerBean) {
		super(scmServerBean);
	}

	protected ISVNClientAdapter getSVNClient(ISVNListener listener) throws Exception {

		SCMServerBean scmServerBean = getScmServerBean();

		ISVNClientAdapter svnClient = SVNClient.getSvnClientAdapter(scmServerBean.getUser(),
				scmServerBean.getPassword());

		if (listener != null) {
			svnClient.setProgressListener(listener);
			svnClient.addNotifyListener(listener);
		}

		return svnClient;
	}

	public String getSVNUrl(ModuleInfoBean moduleInfo) throws Exception {
		StringBuffer url = new StringBuffer();

		SCMServerBean scmServerBean = getScmServerBean();

		url.append(scmServerBean.getProtocol()).append("://");
		url.append(scmServerBean.getHostName());
		if (StringUtils.isNotBlank(scmServerBean.getHostPort())) {
			url.append(":").append(scmServerBean.getHostPort());
		}
		if (StringUtils.startsWith(scmServerBean.getServiceName(), "/")) {
			url.append(scmServerBean.getServiceName());
		} else {
			url.append("/").append(scmServerBean.getServiceName());
		}

		if (moduleInfo != null) {

			if (StringUtils.isNotBlank(moduleInfo.getModule())) {

				if (moduleInfo.getModule().startsWith("/") == false) {
					url.append("/");
				}

				url.append(moduleInfo.getModule());
			}
		}

		return url.toString();
	}

	protected SVNRevision getSVNRevision(ModuleInfoBean moduleInfo) throws Exception {

		SVNRevision svnRevision = SVNRevision.HEAD;

		if (StringUtils.isNotBlank(moduleInfo.getBranch())) {
			svnRevision = SVNRevision.getRevision(moduleInfo.getBranch());
		}

		return svnRevision;
	}

	@Override
	public void importResource(File rootDir, ModuleInfoBean moduleInfo, ISCMListener scmListener) throws Exception {

		SVNListenerAdapter listenerAdapter = scmListener != null ? new SVNListenerAdapter(scmListener) : null;

		ISVNClientAdapter clientAdapter = getSVNClient(listenerAdapter);

		SVNUrl svnUrl = new SVNUrl(getSVNUrl(moduleInfo));

		clientAdapter.doImport(rootDir, svnUrl, "", true);

		if (listenerAdapter != null) {
			listenerAdapter.setTask("checkoutResource");
		}

		SVNRevision svnRevision = getSVNRevision(moduleInfo);

		clientAdapter.checkout(svnUrl, rootDir, svnRevision, true);

	}

	@Override
	public void checkoutResource(File rootDir, ModuleInfoBean moduleInfo, ISCMListener scmListener) throws Exception {

		SVNUrl svnUrl = new SVNUrl(getSVNUrl(moduleInfo));

		SVNRevision svnRevision = getSVNRevision(moduleInfo);

		SVNListenerAdapter listenerAdapter = scmListener != null ? new SVNListenerAdapter(scmListener) : null;

		if (listenerAdapter != null) {
			listenerAdapter.setTask("checkoutResource");
		}

		ISVNClientAdapter svnClient = getSVNClient(listenerAdapter);

		svnClient.checkout(svnUrl, rootDir, svnRevision, true);
	}

	@Override
	public void commitResource(File rootDir, ModuleInfoBean moduleInfo, String comment, ISCMListener scmListener)
			throws Exception {

		SVNListenerAdapter listenerAdapter = scmListener != null ? new SVNListenerAdapter(scmListener) : null;

		if (listenerAdapter != null) {
			listenerAdapter.setTask("commitResource");
		}

		ISVNClientAdapter clientAdapter = getSVNClient(listenerAdapter);

		File[] willAddFiles = getNotInSVNFiles(rootDir, clientAdapter);

		if (ArrayUtils.isEmpty(willAddFiles) == false) {
			for (int i = 0; i < willAddFiles.length; i++) {
				File file = willAddFiles[i];

				if (file.isDirectory()) {
					if (SVNWCUtil.isVersionedDirectory(file) == false) {
						clientAdapter.addDirectory(file, true);
					}

				} else {
					clientAdapter.addFile(file);
				}
			}

		}

		clientAdapter.commit(new File[] { rootDir }, comment, true);

	}

	@Override
	public void commitResource(File rootDir,File[] dirs, ModuleInfoBean moduleInfo, String comment, ISCMListener scmListener)
			throws Exception {

		SVNListenerAdapter listenerAdapter = scmListener != null ? new SVNListenerAdapter(scmListener) : null;

		if (listenerAdapter != null) {
			listenerAdapter.setTask("commitResource");
		}

		List<File> dirList = new ArrayList<>();

		ISVNClientAdapter clientAdapter = getSVNClient(listenerAdapter);

		for (int j = 0; j < dirs.length; j++) {

			File[] willAddFiles = getNotInSVNFiles(dirs[j], clientAdapter);

			if (ArrayUtils.isEmpty(willAddFiles) == false) {

				for (int i = 0; i < willAddFiles.length; i++) {

					File file = willAddFiles[i];


					if (file.isDirectory()) {
						if (SVNWCUtil.isVersionedDirectory(file) == false) {
							dirList.add(file);
							clientAdapter.addDirectory(file, false);
							//System.out.println(file);
						}

					} else {
						boolean hasAdded = false;
						for (Iterator<File> iterator = dirList.iterator(); iterator.hasNext();) {
							File dir = iterator.next();
							if (file.getParentFile().equals(dir)) {
								hasAdded = true;
								break;
							}

						}
						if (hasAdded == false) {
							//System.out.println(file);
							clientAdapter.addFile(file);
						}

					}
				}

			}

		}

		// commitSubFiles(dirs,clientAdapter);

		clientAdapter.commit(dirs, comment, true);

	}

	protected void commitSubFiles(File[] dirs, ISVNClientAdapter clientAdapter) throws Exception {

		for (int j = 0; j < dirs.length; j++) {

			File[] willAddFiles = getNotInSVNFiles(dirs[j], clientAdapter);

			if (ArrayUtils.isEmpty(willAddFiles) == false) {

				for (int i = 0; i < willAddFiles.length; i++) {

					File file = willAddFiles[i];
					// System.out.println(file.getName());
					if (file.isDirectory()) {
						if (SVNWCUtil.isVersionedDirectory(file) == false) {
							clientAdapter.addDirectory(file, false);
						}

						commitSubFiles(file.listFiles(), clientAdapter);

					} else {
						clientAdapter.addFile(file);
					}
				}

			}

		}
	}

	private File[] getNotInSVNFiles(File rootDir, ISVNClientAdapter clientAdapter) throws Exception {

		NotInSVNFileFilter checkFileFilter = new NotInSVNFileFilter(clientAdapter);

		Collection<File> files = FileUtils.listFilesAndDirs(rootDir, checkFileFilter, TrueFileFilter.INSTANCE);

		files.remove(rootDir);

		return FileUtils.convertFileCollectionToFileArray(files);
	}

	@Override
	public void updateResource(File rootDir, ModuleInfoBean moduleInfo, ISCMListener scmListener) throws Exception {

		SVNListenerAdapter listenerAdapter = scmListener != null ? new SVNListenerAdapter(scmListener) : null;

		if (listenerAdapter != null) {
			listenerAdapter.setTask("updateResource");
		}

		ISVNClientAdapter clientAdapter = getSVNClient(listenerAdapter);

		SVNRevision svnRevision = getSVNRevision(moduleInfo);

		clientAdapter.update(rootDir, svnRevision, true);

		// cleanupUnversionedFiles(clientAdapter, new SVNUrl(getSVNUrl(moduleInfo)),
		// rootDir);

	}

	private void cleanupUnversionedFiles(ISVNClientAdapter svnClient, SVNUrl svnURL, File root) throws Exception {

		ISVNDirEntry[] entries = svnClient.getList(svnURL, SVNRevision.HEAD, false);
		for (ISVNDirEntry entry : entries) {
			String fileName = entry.getPath();
			SVNNodeKind nodeType = entry.getNodeKind();
			File localFile = new File(root, fileName);
			if (localFile.exists()) {
				ISVNStatus status = svnClient.getSingleStatus(localFile);
				if (status != null && status.getTextStatus().toInt() != StatusKind.unversioned) {
					if (localFile.isDirectory()) { // see whether there are unversioned files under this dir. Recursive
						String appendPath = "/" + localFile.getName();
						cleanupUnversionedFiles(svnClient, svnURL.appendPath(appendPath), localFile);
						continue; // this is not an unversioned directory, continue
					} else if (localFile.isFile()) {
						continue;
					}
				}

				if (localFile.isFile() && SVNNodeKind.FILE.equals(nodeType)) {
					logger.info("Unversioned file: " + localFile.getPath() + " will be deleted");
					if (!localFile.delete()) {
						logger.error("Unable to delete the file: " + localFile.getPath());
					}
				} else if (localFile.isDirectory() && SVNNodeKind.DIR.equals(nodeType)) {
					logger.info("Unversioned directory: " + localFile.getPath() + " will be deleted");
					try {
						FileUtils.deleteDirectory(localFile);
					} catch (IOException e) {
						logger.error("Error while deleting the directory: " + localFile.getPath(), e);
					}
				}
			}
		}
	}

	@Override
	public void removeResource(File rootDir, File[] files, ModuleInfoBean moduleInfo, String comment,
			ISCMListener scmListener) throws Exception {

		SVNListenerAdapter listenerAdapter = scmListener != null ? new SVNListenerAdapter(scmListener) : null;

		ISVNClientAdapter clientAdapter = getSVNClient(listenerAdapter);

		SVNRevision svnRevision = getSVNRevision(moduleInfo);

		if (listenerAdapter != null) {
			listenerAdapter.setTask("updateResource");
		}

		clientAdapter.update(rootDir, svnRevision, true);

		if (listenerAdapter != null) {
			listenerAdapter.setTask("removeResource");
		}

		clientAdapter.remove(files, true);

		if (listenerAdapter != null) {
			listenerAdapter.setTask("commitResource");
		}

		clientAdapter.commit(new File[] { rootDir }, comment, true);
	}

	@Override
	public void updateResourceToVersion(File rootDir, File file, ResourceUpdateInfo versionInfo,
			ModuleInfoBean moduleInfo, ISCMListener scmListener) throws Exception {

		SVNListenerAdapter listenerAdapter = scmListener != null ? new SVNListenerAdapter(scmListener) : null;

		if (listenerAdapter != null) {
			listenerAdapter.setTask("updateResourceToVersion");
		}

		ISVNClientAdapter clientAdapter = getSVNClient(listenerAdapter);

		String reversion = versionInfo.getRevision();

		Date date = versionInfo.getDate();

		Calendar dateCalendar = Calendar.getInstance();

		dateCalendar.setTime(date);

		Date time = versionInfo.getTime();

		Calendar timeCalendar = Calendar.getInstance();

		timeCalendar.setTime(time);

		Calendar calendar = Calendar.getInstance();

		calendar.set(dateCalendar.get(Calendar.YEAR), dateCalendar.get(Calendar.MONTH), dateCalendar.get(Calendar.DATE),
				timeCalendar.get(Calendar.HOUR_OF_DAY), timeCalendar.get(Calendar.MINUTE),
				timeCalendar.get(Calendar.SECOND));

		SVNRevision svnRevision = SVNRevision.HEAD;

		if (StringUtils.isNotBlank(versionInfo.getRevision())) {
			svnRevision = SVNRevision.getRevision(versionInfo.getRevision());
		}

		if (versionInfo.isUpdateToLastVersion()) {
			svnRevision = SVNRevision.HEAD;
		} else if (StringUtils.isNotBlank(reversion)) {
			svnRevision = SVNRevision.getRevision(versionInfo.getRevision());
		} else if (date != null) {
			svnRevision = SVNRevision.getRevision(DATE_FORMAT.format(calendar.getTime()), DATE_FORMAT);
		}

		clientAdapter.update(file, svnRevision, true);

	}

	@Override
	public VersionLogInformation[] scanResourceLog(final File rootDir, ModuleInfoBean moduleInfo,
			final ShowResourceLogConfig[] resourceLogConfigs, String startRevision, String endRevision,
			final ISCMListener scmListener) throws Exception {

		final SVNListenerAdapter listenerAdapter = scmListener != null ? new SVNListenerAdapter(scmListener) : null;

		if (listenerAdapter != null) {
			listenerAdapter.setTask("scanResourceLog");
		}

		final ISVNClientAdapter clientAdapter = getSVNClient(listenerAdapter);

		// ISVNLogMessage[] logMessages = new ISVNLogMessage[0];

		SVNRevision startVersion;

		SVNRevision endVersion;

		if (StringUtils.isNotBlank(startRevision)) {
			startVersion = startRevision.contains("/")
					? SVNRevision.getRevision(startRevision, new SimpleDateFormat("MM/dd/yyyy"))
					: SVNRevision.getRevision(startRevision);
		} else {
			startVersion = new SVNRevision.Number(1);
		}

		if (StringUtils.isNotBlank(endRevision)) {
			endVersion = endRevision.contains("/")
					? SVNRevision.getRevision(endRevision, new SimpleDateFormat("MM/dd/yyyy"))
					: SVNRevision.getRevision(endRevision);
		} else {
			endVersion = SVNRevision.HEAD;
		}

		final SVNUrl svnUrl = new SVNUrl(getSVNUrl(moduleInfo));

		final ISVNLogMessage[] logMessages = clientAdapter.getLogMessages(svnUrl, startVersion, endVersion, true);

		final List<VersionLogInformation> logInformations = new ArrayList<>();

		final Map<String, long[]> fileDataMap = new HashMap<>();

		int totalCount = logMessages.length;

		int pages;

		int threadCount = 5;

		if (totalCount % threadCount == 0) {
			pages = totalCount / threadCount;
		} else {
			pages = totalCount / threadCount + 1;
		}

		int limit = threadCount;

		int start = 0;

		System.out.println("Total " + totalCount + " Page " + pages);

		for (int currentPage = 1; currentPage <= pages; currentPage++) {

			start = (currentPage - 1) * threadCount;

			if (totalCount < limit) {
				limit = totalCount;
			} else if ((totalCount - start) < limit) {
				limit = totalCount - start;
			}

			CountDownLatch latch = new CountDownLatch(limit);

			Thread[] threads = new Thread[limit];

			for (int i = 0; i < limit; i++) {
				final ISVNLogMessage isvnLogMessage = logMessages[i + start];
				final Integer index = Integer.valueOf(i + start);
				final Thread thread = new WorkThread(latch) {
					@Override
					public void doWork() throws Exception {

						ISVNLogMessageChangePath[] changePaths = isvnLogMessage.getChangedPaths();

						for (int j = 0; fileDataMap.isEmpty() == false && j < changePaths.length; j++) {

							fileDataMap.remove(StringUtils.substringAfter(changePaths[j].getPath(),
									svnUrl.getLastPathSegment() + "/"));
						}

						String outFileName = FilenameUtils.getBaseName(rootDir.getName()) + System.currentTimeMillis()
								+ "diffOut.txt";

						final File outFile = new File(FileUtils.getTempDirectory(), outFileName);

						logInformations.add(packageVersionLogData(rootDir, clientAdapter, svnUrl, outFile,
								isvnLogMessage, resourceLogConfigs, fileDataMap, scmListener));

						if (listenerAdapter != null) {
							listenerAdapter.onProgress(new SVNProgressEvent(
									logMessages.length - getCountDownLatch().getCount(), logMessages.length));
						}

						FileUtils.deleteQuietly(outFile);

					}
				};
				thread.setName("Scan SVN Log Thread[" + (i + start) + "]");
				threads[i] = thread;
			}

			for (int i = 0; i < limit; i++) {
				System.out.println(threads[i].getName());
				threads[i].start();
			}

			latch.await();
		}

		fileDataMap.clear();

		return logInformations.toArray(new VersionLogInformation[logInformations.size()]);

	}

	@Override
	public VersionLogInformation[] showResourceLog(File rootDir, File[] files, ModuleInfoBean moduleInfo,
			final ShowResourceLogConfig[] resourceLogConfigs, String startRevision, String endRevision,
			boolean showFullDetail, ISCMListener scmListener) throws Exception {

		SVNListenerAdapter listenerAdapter = scmListener != null ? new SVNListenerAdapter(scmListener) : null;

		if (listenerAdapter != null) {
			listenerAdapter.setTask("showResourceLog");
		}

		ISVNClientAdapter clientAdapter = getSVNClient(listenerAdapter);

		ISVNLogMessage[] logMessages = new ISVNLogMessage[0];

		SVNRevision start;

		SVNRevision end;

		if (StringUtils.isNotBlank(startRevision)) {
			start = startRevision.contains("/")
					? SVNRevision.getRevision(startRevision, new SimpleDateFormat("MM/dd/yyyy"))
					: SVNRevision.getRevision(startRevision);
		} else {
			start = new SVNRevision.Number(1);
		}

		if (StringUtils.isNotBlank(endRevision)) {
			end = endRevision.contains("/") ? SVNRevision.getRevision(endRevision, new SimpleDateFormat("MM/dd/yyyy"))
					: SVNRevision.getRevision(endRevision);
		} else {
			end = SVNRevision.HEAD;
		}

		if (files[0].isFile()) {
			logMessages = clientAdapter.getLogMessages(files[0], start, end, false);
		} else {

			String filepath = StringUtils.substringAfter(files[0].getPath(), rootDir.getPath());

			logMessages = clientAdapter.getLogMessages(new SVNUrl(getSVNUrl(moduleInfo) + "/" + filepath), start, end,
					true);
		}

		SVNUrl svnUrl = new SVNUrl(getSVNUrl(moduleInfo));

		String outFileName = FilenameUtils.getBaseName(rootDir.getName()) + System.currentTimeMillis() + "diffOut.txt";

		File outFile = new File(FileUtils.getTempDirectory(), outFileName);

		VersionLogInformation[] logInformations = new VersionLogInformation[logMessages.length];

		Map<String, long[]> fileDataMap = new HashMap<>();

		for (int i = 0; i < logMessages.length; i++) {
			ISVNLogMessage isvnLogMessage = logMessages[i];

			if (listenerAdapter != null) {
				listenerAdapter.onProgress(new SVNProgressEvent(i + 1, logMessages.length));
			}

			if (showFullDetail) {

				logInformations[i] = packageVersionLogData(rootDir, clientAdapter, svnUrl, outFile, isvnLogMessage,
						resourceLogConfigs, fileDataMap, scmListener);
			} else {
				VersionLogInformation versionLogInformation = new VersionLogInformation();

				versionLogInformation.setAuthor(isvnLogMessage.getAuthor());

				versionLogInformation.setComment(isvnLogMessage.getMessage());

				versionLogInformation.setRevision(isvnLogMessage.getRevision().toString());

				versionLogInformation.setRevisionTime(isvnLogMessage.getDate());

				logInformations[i] = versionLogInformation;
			}

		}

		fileDataMap.clear();

		FileUtils.deleteQuietly(outFile);

		return logInformations;

	}

	private VersionLogInformation packageVersionLogData(File rootDir, ISVNClientAdapter svnClientAdapter, SVNUrl svnUrl,
			File outFile, ISVNLogMessage logMessage, final ShowResourceLogConfig[] resourceLogConfigs,
			Map<String, long[]> fileDataMap, ISCMListener scmListener) throws Exception {

		ShowResourceLogConfig key = new ShowResourceLogConfig();

		VersionLogInformation versionLogInformation = new VersionLogInformation();

		versionLogInformation.setAuthor(logMessage.getAuthor());

		versionLogInformation.setComment(logMessage.getMessage());

		versionLogInformation.setRevision(logMessage.getRevision().toString());

		versionLogInformation.setRevisionTime(logMessage.getDate());

		ISVNLogMessageChangePath[] changePaths = logMessage.getChangedPaths();

		int totalModifyLineCount = 0;

		File pathFile;

		if (ArrayUtils.isEmpty(changePaths) == false) {

			for (int j = 0; j < changePaths.length; j++) {

				ISVNLogMessageChangePath isvnLogMessageChangePath = changePaths[j];

				SVNUrl url = new SVNUrl(svnUrl.toString()
						+ StringUtils.substringAfter(isvnLogMessageChangePath.getPath(), svnUrl.getLastPathSegment()));

				key.setFileExtension(FilenameUtils.getExtension(isvnLogMessageChangePath.getPath()));

				int index = ArrayUtils.indexOf(resourceLogConfigs, key);

				ShowResourceLogConfig logConfig = index != -1 ? resourceLogConfigs[index] : null;

				int modifyLineCount = 0;

				pathFile = new File(rootDir.getPath()
						+ StringUtils.substringAfter(isvnLogMessageChangePath.getPath(), svnUrl.getLastPathSegment()));

				if (isvnLogMessageChangePath.getAction() != 'D' && pathFile.isFile()) {

					if (logConfig != null && logConfig.isCountLine()) {

						svnClientAdapter.diff(url,
								SVNRevision.getRevision(String.valueOf(logMessage.getRevision().getNumber() - 1)),
								logMessage.getRevision(), outFile, true);
						try {
							List<String> diffLines = FileUtils.readLines(outFile, "UTF-8");
							for (Iterator<String> iterator = diffLines.iterator(); iterator.hasNext();) {
								String line = iterator.next();
								if (StringUtils.startsWith(line, "+") && StringUtils.startsWith(line, "+++") == false) {
									modifyLineCount++;
								}
							}
						} catch (Throwable ex) {
							System.err.println("Can Not Fount File " + outFile);
						}
					}

				}

				totalModifyLineCount += modifyLineCount;

				VersionLogChangePath changePath = new VersionLogChangePath();
				changePath.setChangeLineCount(modifyLineCount);
				changePath.setAction(String.valueOf(isvnLogMessageChangePath.getAction()));
				changePath.setCopySrcPath(isvnLogMessageChangePath.getCopySrcPath());
				if (isvnLogMessageChangePath.getCopySrcRevision() != null) {
					changePath.setCopySrcRevision(isvnLogMessageChangePath.getCopySrcRevision().toString());
				}
				changePath.setPath(isvnLogMessageChangePath.getPath());

				if (isvnLogMessageChangePath.getAction() != 'D' && pathFile.isFile()) {
					// System.out.println(url + " " + logMessage.getRevision());
					try {
						InputStream inputStream = svnClientAdapter.getContent(url, logMessage.getRevision(),
								logMessage.getRevision());

						if (logConfig != null && logConfig.isCountLine()) {
							SourceLineBean sourceLineBean = SourceLineCounter.parse(inputStream);
							changePath.setLineCount(sourceLineBean.getCodeLines());
							changePath.setCommnetLineCount(sourceLineBean.getCommentLines());
							changePath.setBlankLineCount(sourceLineBean.getBlankLines());
						}

						changePath.setFileSize(inputStream.available());

						inputStream.close();
					} catch (Throwable ex) {
						System.err.println("Can Not Fount File " + url);
					}
				}

				versionLogInformation.getChangePaths().add(changePath);
			}
		}

		ISVNLogMessage[] childMessages = logMessage.getChildMessages();

		if (ArrayUtils.isEmpty(childMessages) == false) {
			for (int j = 0; j < childMessages.length; j++) {
				versionLogInformation.getChildVersionLogs().add(packageVersionLogData(rootDir, svnClientAdapter, svnUrl,
						outFile, childMessages[j], resourceLogConfigs, fileDataMap, scmListener));
			}
		}

		versionLogInformation.setModifiedLines(String.valueOf(totalModifyLineCount));

		long totalSize = 0;

		long totalLines = 0;

		try {

			ISVNDirEntry[] dirEntries = svnClientAdapter.getList(svnUrl, logMessage.getRevision(),
					logMessage.getRevision(), true);

			for (int i = 0; i < dirEntries.length; i++) {

				ISVNDirEntry dirEntry = dirEntries[i];

				if ("file".equals(dirEntry.getNodeKind().toString())) {

					long[] fileData = new long[] { 0, 0 };

					scmListener.logMessage(StringUtils.abbreviateMiddle("统计" + dirEntry.getPath(), "...", 50));

					if (fileDataMap.containsKey(dirEntry.getPath())) {
						totalSize += fileDataMap.get(dirEntry.getPath())[0];
						fileData[0] = fileDataMap.get(dirEntry.getPath())[0];
					} else {
						totalSize += dirEntry.getSize();
						fileData[0] = dirEntry.getSize();
					}

					key.setFileExtension(FilenameUtils.getExtension(dirEntry.getPath()));

					int index = ArrayUtils.indexOf(resourceLogConfigs, key);

					ShowResourceLogConfig logConfig = index != -1 ? resourceLogConfigs[index] : null;

					if (logConfig != null && logConfig.isCountLine()) {

						if (fileDataMap.containsKey(dirEntry.getPath())) {
							totalLines += fileDataMap.get(dirEntry.getPath())[1];
							fileData[1] = fileDataMap.get(dirEntry.getPath())[1];
						} else {
							InputStream inputStream = svnClientAdapter.getContent(
									new SVNUrl(svnUrl.toString() + "/" + dirEntry.getPath()), logMessage.getRevision(),
									logMessage.getRevision());
							SourceLineBean sourceLineBean = SourceLineCounter.parse(inputStream);
							totalLines += sourceLineBean.getCodeLines();
							fileData[1] = sourceLineBean.getCodeLines();
						}

					}

					// FileUtils.write(logFile,
					// dirEntry.getPath() + "," + fileData[0] + "," + fileData[1] +
					// SystemUtils.LINE_SEPARATOR, true);

					if (fileDataMap.containsKey(dirEntry.getPath()) == false) {
						fileDataMap.put(dirEntry.getPath(), fileData);

						// System.out.println(logMessage.getRevision() + " add " +
						// dirEntry.getPath() + " " + fileData[0]
						// + "," + fileData[1]);
					}
				}

			}
		} catch (Throwable ex) {
			System.err.println("Can Not Fount File " + svnUrl);
		}

		// FileUtils.write(logFile, "Total ," + totalSize + "," + totalLines,
		// true);

		versionLogInformation.setTotalSize(totalSize);

		versionLogInformation.setTotalLines(totalLines);

		return versionLogInformation;

	}

	@Override
	public ResourceDiffentInfomation showResourceDiffent(File rootDir, File file, String leftVersion,
			String rightVersion, ModuleInfoBean moduleInfo, ISCMListener scmListener) throws Exception {

		final ResourceDiffentInfomation infomation = new ResourceDiffentInfomation();

		infomation.setFileName(FilenameUtils.getName(file.getName()));

		infomation.setFileType(FilenameUtils.getExtension(file.getName()));

		SVNListenerAdapter listenerAdapter = scmListener != null ? new SVNListenerAdapter(scmListener) : null;

		if (listenerAdapter != null) {
			listenerAdapter.setTask("showResourceDiffent");
		}

		ISVNClientAdapter clientAdapter = getSVNClient(listenerAdapter);

		InputStream leftContent = clientAdapter.getContent(file, SVNRevision.getRevision(leftVersion));

		InputStream rightContent = clientAdapter.getContent(file, SVNRevision.getRevision(rightVersion));

		SVNUrl svnUrl = new SVNUrl(getSVNUrl(moduleInfo)
				+ FilenameUtils.normalize(StringUtils.substringAfter(file.getPath(), rootDir.getPath()), true));

		String outFileName = FilenameUtils.getBaseName(file.getName()) + System.currentTimeMillis() + "diffOut.txt";

		File outFile = new File(FileUtils.getTempDirectory(), outFileName);

		clientAdapter.diff(svnUrl, new SVNRevision.Number(Integer.parseInt(leftVersion)),
				new SVNRevision.Number(Integer.parseInt(rightVersion)), outFile, false);

		infomation.setLeftVersion(leftVersion);

		infomation.setRightVersion(rightVersion);

		List<String> leftContents = IOUtils.readLines(leftContent, "UTF-8");

		infomation.setLeftContent(StringUtils.join(leftContents, SystemUtils.LINE_SEPARATOR));

		List<String> rightContents = IOUtils.readLines(rightContent, "UTF-8");

		infomation.setRightContent(StringUtils.join(rightContents, SystemUtils.LINE_SEPARATOR));

		List<DiffDetail> leftDiffDetails = new ArrayList<DiffDetail>();

		List<DiffDetail> rightDiffDetails = new ArrayList<DiffDetail>();

		List<String> lines = FileUtils.readLines(outFile, "UTF-8");

		infomation.setDiffOutput(StringUtils.join(lines, SystemUtils.LINE_SEPARATOR));
		// @@ -29,13 +29,4 @@
		for (Iterator<String> iterator = lines.iterator(); iterator.hasNext();) {
			String line = iterator.next();
			if (StringUtils.startsWith(line, "@@") && StringUtils.endsWith(line, "@@")) { // left

				String diffLinStr = StringUtils.trim(StringUtils.substringBetween(line, "@@"));

				String[] diffLins = StringUtils.split(diffLinStr, " ");

				String[] leftRow = StringUtils.split(diffLins[0], ",");

				String[] rightRow = StringUtils.split(diffLins[1], ",");

				int start = Math.abs(Integer.parseInt(leftRow[0]));

				int end = start + Integer.parseInt(leftRow[1]);

				for (int i = start; i < end; i++) {
					DiffDetail leftDiffDetail = new DiffDetail();
					leftDiffDetail.setContent(leftContents.get(i - 1));
					leftDiffDetail.setLineNumber(i);
					leftDiffDetail.setType(1);
					leftDiffDetails.add(leftDiffDetail);
				}

				start = Math.abs(Integer.parseInt(rightRow[0]));

				end = start + Integer.parseInt(rightRow[1]);

				for (int i = start; i < end; i++) {
					DiffDetail rightDiffDetail = new DiffDetail();
					rightDiffDetail.setContent(rightContents.get(i - 1));
					rightDiffDetail.setLineNumber(i);
					rightDiffDetail.setType(1);
					rightDiffDetails.add(rightDiffDetail);
				}

			}

		}

		FileUtils.deleteQuietly(outFile);

		infomation.setLeftDiffDetails(leftDiffDetails.toArray(new DiffDetail[leftDiffDetails.size()]));

		infomation.setRightDiffDetails(rightDiffDetails.toArray(new DiffDetail[rightDiffDetails.size()]));

		return infomation;

	}

	public Map<String, VersionInfo> getResourceVersionInfos(File rootDir,File[] files) throws Exception {

		final ISVNClientAdapter svnClient = SVNClient.getSvnClientAdapter();

		final Map<String, VersionInfo> map = new HashMap<String, VersionInfo>();

		for (int i = 0; i < files.length; i++) {
			File file = files[i];
			try {
				ISVNInfo svnInfo = svnClient.getInfo(file);

				VersionInfo versionInfo = new VersionInfo();

				versionInfo.setServerType(SCMServerBean.SVN_SERVER);

				versionInfo.setFileName(svnInfo.getFile().getName());
				if (svnInfo.getLastChangedRevision() != null) {
					versionInfo.setRevision(svnInfo.getLastChangedRevision().toString());
				}

				versionInfo.setTimestamp(svnInfo.getLastChangedDate());

				versionInfo.setLastCommitAuthor(svnInfo.getLastCommitAuthor());

				versionInfo.setLocalModified(svnInfo.isCopied() == false);

				map.put(versionInfo.getFileName(), versionInfo);
			} catch (Exception e) {
				// e.printStackTrace();
				VersionInfo versionInfo = new VersionInfo();

				versionInfo.setServerType(SCMServerBean.SVN_SERVER);

				versionInfo.setFileName(file.getName());

				versionInfo.setRevision("?");

				map.put(file.getName(), versionInfo);
			}
		}
		return map;

	}

	public void relocateResource(String from, String to, File rootDir, ModuleInfoBean moduleInfo,
			ISCMListener scmListener) throws Exception {

		final ISVNClientAdapter svnClient = SVNClient.getSvnClientAdapter();

		SVNListenerAdapter listenerAdapter = scmListener != null ? new SVNListenerAdapter(scmListener) : null;

		if (listenerAdapter != null) {
			listenerAdapter.setTask("relocateResource");
		}

		svnClient.relocate(from, to, rootDir.getPath(), true);
	}

	public void switchTo(File rootDir, String svnUrl, ModuleInfoBean moduleInfo, ISCMListener scmListener)
			throws Exception {

		SVNUrl url = new SVNUrl(svnUrl);

		final ISVNClientAdapter svnClient = SVNClient.getSvnClientAdapter();

		SVNListenerAdapter listenerAdapter = scmListener != null ? new SVNListenerAdapter(scmListener) : null;

		if (listenerAdapter != null) {
			listenerAdapter.setTask("switchToResource");
		}

		SVNRevision svnRevision = getSVNRevision(moduleInfo);

		svnClient.switchToUrl(rootDir, url, svnRevision, true);
	}

	public static class SVNListenerAdapter extends DefaultSVNListener {
		private ISCMListener listener;

		private String task;

		public SVNListenerAdapter(String task, ISCMListener listener) {
			super();
			this.task = task;
			this.listener = listener;
		}

		public SVNListenerAdapter(ISCMListener listener) {
			super();
			this.listener = listener;
		}

		public String getTask() {
			return task;
		}

		public void setTask(String task) {
			this.task = task;
		}

		@Override
		public void onProgress(SVNProgressEvent arg0) {
			if (arg0 != null) {
				listener.onProgress(task, arg0.getProgress(), arg0.getTotal());
			}

		}

		@Override
		public void logCommandLine(String arg0) {
			listener.logCommandLine(arg0);
		}

		@Override
		public void logCompleted(String arg0) {
			listener.logCompleted(arg0);
		}

		@Override
		public void logError(String arg0) {
			listener.logError(arg0);
		}

		@Override
		public void logMessage(String arg0) {
			listener.logMessage(arg0);
		}

	}
}
